/*
 * Created by Christopher King
 * Copyright (C) 2007 Christopher King, All Rights Reserved.
 */

package us.cirion.adventure.engine;

import java.util.Enumeration;
import java.util.Vector;

/**
 * A ScriptAction describes an action that can be taken by the player along with
 * its consequences. Each ScriptOption contains zero or more PreRequisites, each
 * of which must be satisfied before the Action can be fulfilled. A ScriptAction
 * also describes the action that must be taken by the player, which always
 * includes a Verb and may optionally include one or two nouns. Finally, the
 * ScriptAction also provides the results of taking the action. Note that by
 * default a ScriptAction may be executed for as many times as the PreRequisites
 * remain valid. If this is not desired, the Result should include some effect
 * to prevent it from being executed again.
 * 
 * @author Chris
 */
public class ScriptAction
{

	Verb verb;
	Noun noun;
	Noun target;
	Vector preRequisites;
	Vector results;

	/**
	 * Constructs a new instance of ScriptAction with no requirements or
	 * effects.
	 */
	public ScriptAction()
	{
		results = new Vector();
		preRequisites = new Vector();
	}

	/**
	 * Retrieves the Verb for this ScriptAction. This describes the action the
	 * player must command in order to create the effects.
	 * 
	 * @return The Verb for this ScriptAction.
	 */
	public Verb getVerb()
	{
		return verb;
	}

	/**
	 * Sets the Verb for this ScriptAction.
	 * 
	 * @param verb The Verb for this ScriptAction.
	 */
	public void setVerb(Verb verb)
	{
		this.verb = verb;
	}

	/**
	 * Retrieves the Noun for this ScriptAction. The Noun usually corresponds to
	 * the direct object in a simple sentence. For example, the commands "Look
	 * Fire" and "Eat Fish" both have a noun in the second position. The Noun is
	 * an optional component of a ScriptAction.
	 * 
	 * @return The Noun for this ScriptAction, or <code>null</code> if none is
	 *         needed.
	 */
	public Noun getNoun()
	{
		return noun;
	}

	/**
	 * Sets the Noun for this ScriptAction. If none is provided, the action may
	 * still execute with only a Verb.
	 * 
	 * @param noun The Noun for this ScriptAction.
	 */
	public void setNoun(Noun noun)
	{
		this.noun = noun;
	}

	/**
	 * Retrieves the Target for this ScriptAction. The Target usually
	 * corresponds to the indirect object in a simple sentence. For example, in
	 * the commands "Give book to librarian" and "Paint fence with brushes", the
	 * Target is the last word in the sentence. The Target is an optional
	 * component of a ScriptAction, and will not be considered if no Noun is
	 * provided.
	 * 
	 * @return The Target for this ScriptAction, or <code>null</code> if none
	 *         is needed.
	 */
	public Noun getTarget()
	{
		return target;
	}

	/**
	 * Sets the Target for this ScriptAction. If none is provided, the action
	 * may still execute with a Verb and, if provided, a Noun.
	 * 
	 * @param target The Target for this ScriptAction.
	 */
	public void setTarget(Noun target)
	{
		this.target = target;
	}

	/**
	 * Add a condition that must be satisfied before this Action can be
	 * performed. For example, the player might need to have defeated a monster
	 * before he can claim a reward from the king. If no prerequisite is given,
	 * the action may always be performed. Note that in addition to meeting the
	 * prerequites, the player must be able to access the noun and target in the
	 * action: for example, the player will not be allowed to strike the monster
	 * if the monster is not present.
	 * 
	 * @param preRequisite Another condition to be satisfied.
	 */
	public void addPreRequisite(PreRequisite preRequisite)
	{
		preRequisites.addElement(preRequisite);
	}

	/**
	 * Add an effect that will take place once this Action is performed. For
	 * example, the player might receive a large purse of gold, or be teleported
	 * to a new land.
	 * 
	 * @param result The effect to take place.
	 */
	public void addResult(Result result)
	{
		results.addElement(result);
	}

	/**
	 * Tests whether the provided noun matches the requirements of this action.
	 * Some actions may require that no noun be provided, others will accept any
	 * noun, and still more will require one particular noun.
	 * 
	 * @param testNoun The noun to compare.
	 * @return <code>true</code> if the provided noun applies to this action,
	 *         <code>
	 * false</code> otherwise.
	 */
	public boolean matchesNoun(Noun testNoun)
	{
		return matches(this.noun, testNoun);
	}

	/**
	 * Tests whether the provided target matches the requirements of this
	 * action. Some actions may require that no target be provided, others will
	 * accept any target, and still more will require one particular target.
	 * 
	 * @param target The target to compare.
	 * @return <code>true</code> if the provided target applies to this
	 *         action, <code>
	 * false</code> otherwise.
	 */
	public boolean matchesTarget(Noun target)
	{
		return matches(this.target, target);
	}

	private boolean matches(Noun noun, Noun testNoun)
	{
		if (testNoun == null && noun == null)
		{
			return true;
		} else if (noun != null && noun.equals(testNoun))
		{
			return true;
		}
		return false;
	}

	/**
	 * Indicates whether all prerequisite conditions for the execution of this
	 * action have been met. For example, the player may need to slay a monster
	 * and rescue the princess before a reward can be claimed. This method will
	 * return <code>true
	 * </code> only if all those conditions have been met.
	 * 
	 * @return <code>true</code> if all conditions have been met to execute
	 *         this action, <code>false</code> otherwise.
	 */
	public boolean isPreRequisiteSatisfied()
	{
		boolean passesAll = true;
		Enumeration preReqs = preRequisites.elements();
		while (preReqs.hasMoreElements())
		{
			PreRequisite preRequisite = (PreRequisite) preReqs.nextElement();
			if (!preRequisite.isSatisfied())
			{
				passesAll = false;
				break;
			}
		}
		return passesAll;
	}

	/**
	 * Carries out all the results that have been assigned to this action.
	 */
	public void executeResults()
	{
		Enumeration resultsEnum = results.elements();
		while (resultsEnum.hasMoreElements())
		{
			Result result = (Result) resultsEnum.nextElement();
			result.execute();
		}
	}

}
